// 基础类型推论
// TypeScript里,在有些没有明确指出类型的地方,
// 类型推论会帮助提供类型
var x = 3;
x = "S";
// 此处虽然没有直接声明x的变量类型
// 但是因为x被赋值为数字3
// 那么x变量的数组类型就会被自动限制为数字
// 这种推断发生在初始化变量和成员,设置默认参数值和决定函数返回值时
// 最佳通用类型
// 但需要从几个表达式中推断类型时候,
// 会使用这些表达式的类型来推断出一个最合适的通用类型
var y = [0, 1, null];
// 此处的类型推论分两步
// 第一步,变量x的值必须是数组
// 第二步,该数组的每个项目必须的得是数字
// 于是x得类型就是x:number[]
// 报错,不允许玩该数组内添加字符串
y.push("1");
// 为了推断x得类型
// 我们必须考虑所有元素得类型
// 这里有两种选择:number和null
// 计算通用耐心算法会开率所有的候选类型
// 并给出一个兼容是所有候选类型的类型
// 由于追踪的通用类型取自候选类型
// 有些时候候选类型共享相同的通用类型,
// 但是却没有一个类型能作为所有候选类型的类型
var Rhino = /** @class */ (function () {
    function Rhino() {
    }
    return Rhino;
}());
var Elephant = /** @class */ (function () {
    function Elephant() {
    }
    return Elephant;
}());
var Snake = /** @class */ (function () {
    function Snake() {
    }
    return Snake;
}());
var zoo = [new Rhino(), new Elephant(), new Snake()];
// 这里我们想让zoo被推断为Animal[]类型,
// 但是这个数组里没有对象是Animal类型的
// 因此不能推断出这个结果
// 为了更正,当候选类型不能使用的时候我们需要明确的指出类型
var zoo2 = [new Rhino(), new Elephant(), new Snake()];
// 如果没有找到最佳推断的结果为联合数组类型
// (Rhino,Elephant,Snake)[]
// 上下文类型推论(上下文推论)
// TypeScript类型推论也可能按照相反的方向进行
// 这叫做"按上下文归类"
// 按上下文归类会发生在表达式的类型与所处的位置相关时
// Typescript 类型检查器使用window.onmousedown函数的类型
// 来推断右边函数表达式的类型
// 因此,就能推断出mouseEvent参数的类型了
window.onmousedown = function (mouseEvent) {
    console.log(mouseEvent.button);
};
// 如果上下文类型表达式包好了明确的类型信息,上下文类型被忽略
window.onmousedown = function (mouseEvent) {
    console.log(mouseEvent.button);
};
// 上下文归类会在很多情况下使用到
// 通常包含函数的参数
// 赋值表达式的右边
// 类型断言
// 对象成员和数组字面量和返回值语句
// 上下文类型也会作为最佳通用类型的候选类型
function createZoo() {
    return [new Rhino(), new Elephant(), new Snake()];
}
// 这个例子里,最佳通用类型有4个候选者:Animal, Rhino, Elephant, Snack 
// 当然Animal会被作通用类型
// 基础类型推论
// TypeScript里，在有些没有明确指出类型的地方类型推论会帮助提供类型
var x = 3;
// 此处虽然没有直接声明x变量的值的类型，但是因为x被赋值为数字3
// 那么x变量的数据类型就会被自动限制为数字
x = 's'; //报错，x变量只能存储数字类型
var Person = /** @class */ (function () {
    function Person() {
    }
    return Person;
}());
var Mother = /** @class */ (function () {
    function Mother() {
    }
    return Mother;
}());
var p;
p = new Person();
var x;
var y = { name: 'Alice', location: 'Seattle' };
x = y;
// 这里要检查y能否复制给x，编译器检查中的每个属性，看是否能在y中也找到对应属性
// 在这个例子中,y必须包含名字是name的string类型成员,y满足条件,因此复制正确
function greet(n) {
    console.log('Hello,' + n.name);
}
greet(y);
// 注意,y有个额外的location属性,但这不会引发错误,只有目标类型(这里是Named)的成员会被一一检查是否兼容
// 这个比较过程说是递归进行的,检查每个成员以及子成员
// 类型系统强制原函数的返回值类型必须是目标函数返回函数的子类型
// 可选参数以及剩余参数
// 比较函数兼容性的时候,可选参数与必须参数是可呼唤的,源类型上有额外的可选参数不是错误,
// 目标类型的可选参数在源类型里没有对应的参数也不是错误
function invokeLater(args, callback) {
}
invokeLater([1, 2], function (x, y) { return console.log(x + ',' + y); });
invokeLater([1, 2], function (x, y) { return console.log(x + ',' + y); });
// 当一个函数有剩余参数时,它被当作无限个可选参数,这对于类型系统来说不是稳定的,
// 但从运行时的角度来看,可选参数一般来说是不强制的,因为对于大多数函数来说相当于传递了一些undefined
// 枚举类型的比较
// 枚举类型与数字类型兼容,并且数字类型与枚举类型兼容,不同枚举类型之间是不兼容的
var Status;
(function (Status) {
    Status[Status["Ready"] = 0] = "Ready";
    Status[Status["waiting"] = 1] = "waiting";
})(Status || (Status = {}));
;
var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["blue"] = 1] = "blue";
    Color[Color["Green"] = 2] = "Green";
})(Color || (Color = {}));
;
var s = Status.Ready;
s = 3;
s = Color.Green;
// 类的类型比较
// 类与对象字面量和接口差不多, 但有一点不同:类有静态部分和实力部分的类型
// 比较两个类类型的对象时,只有实例成员会被比较.静态长远和构造函数不在比较的范围内
var Animal4 = /** @class */ (function () {
    function Animal4(name, numFeet) {
    }
    Animal4.sayMyName = function () {
        console.log("hello");
    };
    return Animal4;
}());
var Size = /** @class */ (function () {
    function Size(numFeet) {
    }
    return Size;
}());
var a;
var s2;
a = s2;
s2 = a;
// 类的私有成员和受保护成员会影响兼容性
// 当检查类实力的兼容时,如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的私有成员
// 同样的,这条规则也使用与包含受保护成员实力的类型检查,
// 这允许字类赋值给父类,但是不能赋值给其他有同样类型的类
